package me.zhengjie.rest;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.annotation.rest.AnonymousPostMapping;
import me.zhengjie.dto.Web3jDto;
import me.zhengjie.enums.BlockChainEnum;
import me.zhengjie.service.BlockChainService;
import me.zhengjie.service.Web3jService;
import me.zhengjie.utils.Convert;
import me.zhengjie.utils.EthUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.DynamicArray;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.Uint;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.crypto.RawTransaction;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
import java.util.function.Function;

/**
 * 基于以太坊衍生链的web3j接口
 */
@Slf4j
@RestController
@RequestMapping("/api/web3j")
@RequiredArgsConstructor
@Api(tags = "web3j：web3j接口")
public class Web3jController {

    private final Web3jService web3jService;
    private final BlockChainService blockChainService;

    @ApiOperation("测试接口")
    @AnonymousPostMapping("/testTransfer")
    public Map<String, Object> testTransfer() {
        Web3jDto web3jDto = new Web3jDto();
        web3jDto.setWeb3j(EthUtils.getWeb3jService(BlockChainEnum.ETH_FRX_TEST));
        web3jDto.setGasPrice(blockChainService.getGasPrice(EthUtils.getWeb3jService(BlockChainEnum.ETH_FRX_TEST)));
        web3jDto.setGasLimit(BlockChainEnum.ETH_FRX_TEST.getGasLimit());
        web3jDto.setChainId(BlockChainEnum.ETH_FRX_TEST.getChainId());

        // 调用 添加流动性方法
//        BigDecimal amountIn = new BigDecimal("50");
//        BigDecimal amountOutMin = new BigDecimal("0.9");
//        List<Address> addressList = new ArrayList<>();
//        addressList.add(new Address("0x4A2337793317A381F2D077CDBAb594F4D7875B87"));
//        addressList.add(new Address("0x1b161852dC4d8Ce1ddC54994A82D08Eb9D3E3D10"));
//        String to = "0xb464218bD738ceaDEE0845B6100a2b31DFFeD09d";
//        BigInteger deadline = new BigInteger("1753059817");
//        String contractAddress = "0x1D2d5389e8c3736962cAF63CBc9E74A1A67A7CA5"; // 路由合约地址
//        Function<BigInteger, RawTransaction> function = swapExactTokensForETH(web3jDto,amountIn, amountOutMin, addressList, to, deadline, contractAddress);

        // 调用 原生币转账方法
        BigDecimal value = new BigDecimal("100");
        String toAddress = "0x2D90A171c62A0D2033d3aED05a1DF2AEd2815E5b";
        Function<BigInteger, RawTransaction> function = transfer(web3jDto, value, toAddress);

        // 获取hash结果
        String privateKey = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("ethSendTransaction.transactionHash", web3jService.sendTransaction(web3jDto, blockChainService.getCredentials(privateKey), function));
        return resultMap;
    }

    /**
     * 调用方法：添加流动性
     * @param web3jDto
     * @param amountIn
     * @param amountOutMin
     * @param addressList
     * @param to
     * @param deadline
     * @param contractAddress
     * @return
     */
    private Function<BigInteger, RawTransaction> swapExactTokensForETH(Web3jDto web3jDto, BigDecimal amountIn, BigDecimal amountOutMin, List<Address> addressList, String to, BigInteger deadline, String contractAddress) {
        Function<BigInteger, RawTransaction> function = (nonce) -> {
            String methodName = "swapExactTokensForETH";

            Convert.Unit unitA = Convert.Unit.getUnitByFactor(6);
            Convert.Unit unitB = Convert.Unit.getUnitByFactor(18);

            BigInteger bigAmountIn = Convert.toWei(amountIn, unitA).toBigInteger();
            BigInteger bigAmountOutMin = Convert.toWei(amountOutMin, unitB).toBigInteger();

            List<Type> inputParameters = Arrays.asList(
                    new Uint(bigAmountIn),
                    new Uint(bigAmountOutMin),
                    new DynamicArray<>(Address.class, addressList),
                    new Address(to),
                    new Uint(deadline));
            List<TypeReference<?>> outputParameters = Arrays.asList(
                    new TypeReference<DynamicArray<Uint256>>() {
                    }
            );
            String data = EthUtils.builderData(methodName, inputParameters, outputParameters);
            return RawTransaction.createTransaction(nonce, web3jDto.getGasPrice(), web3jDto.getGasLimit(), contractAddress, data);
        };
        return function;
    }

    /**
     * 原生币转账
     * @param web3jDto
     * @param value
     * @param toAddress
     * @return
     */
    private Function<BigInteger, RawTransaction> transfer(Web3jDto web3jDto, BigDecimal value, String toAddress) {
        Function<BigInteger, RawTransaction> function = (nonce) -> {
            //将金额转化为以WEI为单位
            BigInteger val = Convert.toWei(value, Convert.Unit.ETHER).toBigInteger();
            //创建交易
            return RawTransaction.createEtherTransaction(nonce, web3jDto.getGasPrice(), web3jDto.getGasLimit(), toAddress, val);
        };
        return function;
    }

    /**
     * erc20代币转账
     * @param web3jDto 连接链
     * @param erc20Address erc20代币地址
     * @param transferValue 转账金额
     * @param toAddress 转入地址
     * @return
     */
    private Function<BigInteger, RawTransaction> erc20Transfer(Web3jDto web3jDto, String erc20Address, BigDecimal transferValue, String toAddress) {
        Function<BigInteger, RawTransaction> function = (nonce) -> {
            // 方法名
            String methodName = "transfer";

            //将金额转化为以 MWEI 为单位，USDR 精度 6 位
            BigInteger value = org.web3j.utils.Convert.toWei(transferValue, org.web3j.utils.Convert.Unit.MWEI).toBigInteger();

            //构建调用erc20合约的data
            List<Type> inputParameters = Arrays.asList(
                    new Address(toAddress),
                    new Uint256(value)
            );

            // 创建交易数据
            String data = EthUtils.builderData(methodName, inputParameters, Collections.emptyList());
            return RawTransaction.createTransaction(nonce, web3jDto.getGasPrice(), web3jDto.getGasLimit(), erc20Address, data);
        };
        return function;
    }

}
